package notentool;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.CookieHandler;
import java.net.CookieManager;
import java.net.CookiePolicy;
import java.net.CookieStore;
import java.net.HttpCookie;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.jdom2.Attribute;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;

/* 
 * Editors: Tim Malich, 
 * Load and manipulate the data from https://wwwqis.htw-dresden.de/
 */
public class QISDataHandler {
	private int matrikel;
	private String pw; // TODO encrypt!
	public boolean DEBUG=false; 
	private String studiengang=null;
	private String fachsemester=null;
	private int sumCredits=0;
	private double avg=0;
	private double wavg=0;
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private ArrayList <Module> moduleList = new ArrayList();
	public int progress=0; // progress state, in percent, while using loadDataFromQIS(). 0=not loading/finish
	private ArrayList<SemNote> semNoten = new ArrayList<SemNote>();
	
	class Module{
		private String id; // id for our datastructure (modulID_datum)
		private int modulID; // id from htw site
		private String modulText;
		private String semester;
		private float note;
		private boolean status; 
		private float credits;
		private float ects;
		private String vermerk;
		private int versuch;
		private String datum;
		private Boolean isNew; // default is true
		
		public void addModul(int modulID, String modulText, String semester, float note, boolean status, float credits, float ects, String vermerk, int versuch, String datum, Boolean isNew){
			this.modulID=modulID;
			this.modulText=modulText;
			this.semester=semester;
			this.note=note;
			this.status=status;
			this.credits=credits;
			this.ects=ects;
			this.vermerk=vermerk;
			this.versuch=versuch;
			this.datum=datum;
			this.id = Integer.toString(modulID).concat(datum);
			this.isNew = isNew;
		}
		
		public Module deepcopyModul(Module m){
			Module copyTarget;
			if( null == m )
				copyTarget = new Module( modulID, modulText,  semester,  note,  status, credits, ects, vermerk, versuch, datum, isNew );
			else
				copyTarget=new Module( m.modulID, m.modulText,  m.semester,  m.note,  m.status, m.credits, m.ects, m.vermerk, m.versuch, m.datum, m.isNew );
			return copyTarget;
		}
		public Module deepcopyModul(){
			return deepcopyModul(null);
		}
		
		public Module(int modulID, String modulText, String semester, float note, boolean status, float credits, float ects, String vermerk, int versuch, String datum){
			addModul(modulID, modulText, semester, note, status, credits, ects, vermerk, versuch, datum, true);
		}
		public Module(int modulID, String modulText, String semester, float note, boolean status, float credits, float ects, String vermerk, int versuch, String datum, Boolean isNew){
			addModul(modulID, modulText, semester, note, status, credits, ects, vermerk, versuch, datum, isNew);
		}
		public Module(){
			addModul(0, null, null, 0, false, 0, 0, null, 0, null, true);
		}
	}

	// helper class for semester statistics
	// helper class for semester statics
	class SemNote{
		private float noten;     // sum noten*credits
		private String semester; // titel
		private float credits;   // sum credits
		SemNote(String semester, float note, float credits){
			this.semester=semester; // name of semester
			add(note,credits);
		}
		// adds a new mark to the semester
		public void add(float note, float credits){
			this.noten=noten+(note*credits);
			this.credits+=credits;
		}
		// returns the average of the semester
		public float getAvg(){
			return 0==credits?0:noten/credits;
		}
		public String getSemester(){ 
			return semester;
		}
		public float getCredits(){
			return credits;
		}
	}
	/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Constructors ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
	
	public QISDataHandler(int matrikel, String pw){
		this.matrikel=matrikel;
		this.pw=pw;
	}

	/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Getters and Setter ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	public int getMatrikel(){
		return this.matrikel;
	}
	
	public void setMatrikel( int matrikel){
		this.matrikel = matrikel;
	}
	
	/* returns false on failure else true 
	 * doesn't change the last matrikel number on failure
	 * */
	public boolean setMatrikel( String matrikel ){
		try{
			int matrikel_tmp=Integer.parseInt(matrikel);
			this.matrikel = matrikel_tmp;
		}catch( NumberFormatException e){
			Errors.addError("Matrikelnr >" + matrikel +"< ist keine gültige Nummer", 17);
			return false;
		}
		
		return true;
	}
	
	public void setPw(String pw ){
		this.pw = pw;
	}
	
	/* return the string Studiengang */
	public String getStudiengang(){
		return this.studiengang;
	}
	
	/* return the calculated average if it is not 0 at the moment */
	public double getAvg(){
		if( 0 == avg )
			avg=calcAverage();
		return avg;
	}
	
	/* return the calculated weighted average if it is not 0 at the moment */
	public double getWavg(){
		if( 0 == wavg )
			wavg=calcWeightedAverage();
		return wavg;
	}
	
	/* return the summation of credits if it is not 0 at the moment */
	public double getSumCredits(){
		if( 0 == sumCredits )
			sumCredits=calcSumCredits();
		return sumCredits;
	}
	
	public ArrayList<SemNote> getSemNoten(){
		return semNoten;
	}
	/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ real Methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
	
	/* return true if the module already exist in the module ArrayList (parameter module, not this.module!)
	 * else false
	 */
	
	//TODO findet nach dem laden alle Module als neu!!!!
	private Boolean modulExist(Module mp, ArrayList <Module> modules ){ //mp=Module via Parameter
		
		for( Module m : modules ){
			if( m.id.contentEquals(mp.id) ){
				if( m.modulText.contentEquals(mp.modulText) &&
					m.vermerk.contentEquals(mp.vermerk)		&&
					m.semester.contentEquals(mp.semester)	&&
					m.datum.contentEquals(mp.datum)			&&
					m.note 		== mp.note    				&&
					m.status	== mp.status				&&
					m.credits	== mp.credits   			&&
					m.ects		== mp.ects					&&
					m.versuch	== mp.versuch )	{
						return true;
					}
			}
		}
		return false;
	}
	
	//DEPRECATED 
	private Boolean modulExist(int modulID, String datum, ArrayList <Module> module){
		for( Module m : module ){
			if( m.modulID == modulID){
				if( null == datum || m.datum.matches(datum)	)
					return true;
			}
		}
		return false;
	}
	
	@SuppressWarnings("unused")
	private Boolean modulExist(int modulID, ArrayList <Module> module){
		return modulExist(modulID, null, module);
	}
	/* return true if the module already exist in the module ArrayList
	 * else false
	 */
	@SuppressWarnings("unused")
	private Boolean modulExist(int modulID, String datum){
		return modulExist(modulID, datum, this.moduleList);
	}
	// look @ moduleExist(int, String);
	@SuppressWarnings("unused")
	private Boolean modulExist(int modulID){
		return modulExist(modulID, null, this.moduleList);
	}
	private Boolean modulExist(String id, ArrayList <Module> module){
		for( Module m : module ){
			if( m.id == id )
				return true;
		}
		return false;
	}
	@SuppressWarnings("unused")
	private Boolean modulExist( String id ){
		return modulExist(id, this.moduleList);
	}
	/* login and parse the data from the qis portal
	 * return true if everything is fine, else false 
	 * attention: ignore https Certificates
	 *    this.ignoreCertificates();
	 * uses addModuls(String);
	 * creates 3 files: Noten-Bachelor.html, Noten-Master.html, Noten-Diplom.html
	 * used Error Codes: 1, 2, 4, 15 
	*/
	public Boolean loadDataFromQIS(){
		ArrayList<Module> modulelist_tmp = deepcopyModule(); 
		String link=null;
		String linkBachelor=null;
		String linkDiplom=null;
		String linkMaster=null;
		String linkNormal=null;
		// create CookieManager
		CookieManager manager = new CookieManager();
		manager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
		CookieHandler.setDefault(manager);
		CookieStore cookieJar = manager.getCookieStore();
		
		this.progress=5;
		System.out.println("Lade noten vom QIS-Portal.");
		System.out.print("Fortschritt: [               ]\r");
		System.out.flush();
		
		try {
			// 1) login and manage cookies
			
			URL url = new URL("https://wwwqis.htw-dresden.de/qisserver/rds" +
					"?state=user&type=1&category=auth.login&startpage=portal.vm" +
					"&username=" + this.matrikel +
					"&password=" + this.pw + "&submit=Ok");
			URLConnection urlConn = url.openConnection();
			urlConn.getContent(); // sets in the background the cookies in the CookieManger ~ very important!
			cookieJar = manager.getCookieStore();
			
			BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
			
			// 2) find the notenspiegel link
			for(String line=reader.readLine(); line != null; line=reader.readLine() ) {
				if( null != (link=grepSpecialLink("notenspiegel", line)) )
					break;
				
				// check if the username and password are correct
				if ( true == Pattern.matches(".*Anmeldung fehlgeschlagen.*", line) ){
					Errors.addError("Anmeldung am QIS-Portal fehlgeschlagen, falscher Nutzername oder falsches Passwort!", 1);
					System.out.println( Errors.getLastErrorMsg());
					return false; // note, restore ArrayList moudlelist is not necessary at this point
				}
			}
			System.out.print("Fortschritt: [=              ]\r");
			this.progress=10;
			// other error or success messages
			if ( null == link ){
				Errors.addError("Allgemeiner fehler beim Anmelden!", 2);
				System.out.println(Errors.getLastErrorMsg());
				return false;
			}
			if(DEBUG)
				System.out.println("Erfolgreich eingeloggt und Notenspiegellink gefunden.\n  > " + link);
	        reader.close();
	        
	        System.out.print("Fortschritt: [==             ]\r");
	        this.progress=20;
		}catch (MalformedURLException e) {
			Errors.addError("Die URL >" + link +
					"< konnte nicht aufgerufen werden.\n Eventuell haben Sie keine Internetverbindung " +
					"oder der QIS-Server ist mal wieder nicht zu erreichen.", 15);
			if( DEBUG )
				e.printStackTrace();
			return false;
		}catch (IOException e) {
			Errors.addError("Allgemeiner fehler beim Anmelden!", 2);
			if(DEBUG) 
				e.printStackTrace();
			return false;
		}
		
		try {
			// 3. grep the master, bachelor, diplom links
			URL url = new URL(link);
			URLConnection urlConn = url.openConnection();
			// set multiple cookies
			for( HttpCookie cookie : cookieJar.getCookies()){
				urlConn.setRequestProperty("Cookie", cookie.toString());
			}
			urlConn.getContent();
			
			System.out.print("Fortschritt: [===            ]\r");
			System.out.flush();
			this.progress=30;
			BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
			for(String line=reader.readLine(); line != null; line=reader.readLine() ) {
				link = grepSpecialLink("abschl=", line);
				
				if ( null != link ){
					// decide between dipl/ master/ bachelor
					if( true == Pattern.matches(".*Diplom.*", line) ){
						linkDiplom=link;
						if( DEBUG )
							System.out.println("Diplomnotenlink gefunden: \n > " + linkDiplom);
						
					}
					else if ( true == Pattern.matches(".*Bachelor.*", line) ){
						linkBachelor=link;
						if( DEBUG )
							System.out.println("Bachelornotenlink gefunden: \n > " + linkBachelor);
					}
					else if ( true == Pattern.matches(".*Master.*", line ) ){
						linkMaster=link;
						if( DEBUG )
							System.out.println("Masternotenlink gefunden: \n > " + linkMaster);
					}
					else if( true == Pattern.matches(".*Notenspiegel.*", line) ){
						linkNormal=link;
						if( DEBUG )
							System.out.println("Notenspiegellink gefunen: \n >" + linkNormal);
					}
					else{
						Errors.addError("Ein gefundener Link konnte weder dem Master, dem Diplom noch dem Bachelor Studiengang zugeordnet werden!", 4);
						//return false;
					}	
				}
				link=null;
			}
			reader.close();
			
			System.out.print("Fortschritt: [====           ]\r");
			System.out.flush();
			this.progress=40;
			// 4. load/ save the bachelor, master, diplom sites
			
			System.out.print("Fortschritt: [========       ]\r");
			System.out.flush();
			this.progress=50;
			String filename=null;
			if( null != linkMaster ){
				filename = "Noten-Master.html";
				savePageSource(linkMaster, filename);
		        if( DEBUG )
		        	System.out.println(filename + " gespeichert!");
		        addModulsFromHTML("file:" + filename); 
			}
			
			System.out.print("Fortschritt: [==========     ]\r");
			this.progress=60;
			System.out.flush();
			if( null != linkBachelor ){
				filename="Noten-Bachelor.html";
				savePageSource(linkBachelor, filename);
				this.progress=65;
				if( DEBUG )
		        	System.out.println(filename + " gespeichert!");
		        addModulsFromHTML("file:"+filename);
		        
			}
			System.out.print("Fortschritt: [============   ]\r");
			System.out.flush();
			this.progress=70;
			if( null != linkDiplom ){
				filename="Noten-Diplom.html";
				savePageSource(linkDiplom, filename);
				this.progress=75;
		        if( DEBUG )
		        	System.out.println(filename + " gespeichert!");
		        addModulsFromHTML("file:"+filename);
			}
			
			System.out.print("Fortschritt: [=============  ]\r");
			System.out.flush();
			this.progress=80;
			if( null != linkNormal ){
				filename="Noten.html";
				savePageSource(linkNormal, filename);
				this.progress=85;
				if( DEBUG )
					System.out.println(filename + " gespeichert!");
				addModulsFromHTML("file:"+filename);
			}
		
			System.out.print("Fortschritt: [============== ]\r");
			System.out.flush();
			this.progress=98;
		}catch (MalformedURLException e) {
			Errors.addError("Die URL >" + link +
					"< konnte nicht aufgerufen werden.\n Eventuell haben Sie keine Internetverbindung " +
					"oder der QIS-Server ist mal wieder nicht zu erreichen.", 15);
			if( DEBUG )
				e.printStackTrace();
			
			if( null != modulelist_tmp )
				this.moduleList = modulelist_tmp;
			return false;
		}catch (IOException e) {
			Errors.addError("Allgemeiner fehler beim Anmelden!", 2);
			if(DEBUG) 
				e.printStackTrace();
			
			if( null != modulelist_tmp )
				this.moduleList = modulelist_tmp;
			return false;
		}
		calcAll(); // finally done, calc statistics
		System.out.print("Fortschritt: [===============]\n");
		this.progress=0;
		return true;
	}
		
	/* give a html file to insert the table content
	 * sets neueNoten to true if it has added new marks to the module list
	 * attention, if the line is to long, the method will crash. // TODO avoid this!
	 *  */
	public boolean addModulsFromHTML( String file ) throws IOException{
		URL url = new URL(file);
		BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
		String line=reader.readLine();
		
		ArrayList<ArrayList<ArrayList<String>>> tableList = new ArrayList<ArrayList<ArrayList<String>>>();
		
		//TODO it looks complicated, but it's very simple. -> explain with a graphic
		//TODO take an html Parser for the next version!
		while( null != line ){ // Document Loop
			
			if( null != line && Pattern.matches(".*<table.*", line) ){ // table found
				ArrayList<ArrayList<String>> trList = new ArrayList<ArrayList<String>>();
				
				// repeate until the end tag of the table was found
				while( null != line && !Pattern.matches(".*</table>.*?", line) && !Pattern.matches(".*<table.*/>.*?", line) ){
					
					// 2) find the tr elements in the table
					if( null != line && Pattern.matches(".*<tr.*", line) ){ // tr element found
						ArrayList <String> tdList  = new ArrayList<String>();
						
						while( null != line && !Pattern.matches(".*</tr>.*?", line) && !Pattern.matches(".*<tr.*/>.*?", line) ){ // 3) find the td elements
						
							line=reader.readLine();	
							if( Pattern.matches(".*<t[d|h].*?", line) ){ // td element found
								String tdString = line;
								do{ // press everything from <td> until </td> in one line
									if( null == line ||  Pattern.matches(".*</t[d|h]>.*", line) || Pattern.matches(".*<t[d|h].*/>.*", line) ){
										tdString += line;
										break;
									}
									line=reader.readLine();
									//System.out.println("LINE: " + line);
									tdString+= line;
								} while( true );
								// cut the content from the tdString
								tdString=grepString(">.*?[/td|/th|/]>",tdString); // grep the part between > and ( </td> or </th> or /> )
								tdString=tdString.replace("	", ""); // remove the tabs
								tdString=tdString.replaceAll("<!--.*-->", ""); // remove comments !regex only in replaceAll possible!
								tdString=tdString.replace("&nbsp;", ""); // remove &nbsp;
								while( tdString.contains("  ") ) // remove double spaces
									tdString=tdString.replace("  "," ");
								//TODO ä, ö ü, ß 
								tdString=tdString.substring(1,tdString.length()-5); // cut the > and the </td>
								tdList.add(tdString);								
							}
	
						}
						trList.add( tdList );
					}
					
					line=reader.readLine();	
				}
				tableList.add(trList);
				
			}
			
			line=reader.readLine();
		}
		
		// we are looking for "N o t e n s p i e g e l" and take the next but one element
		for( int j=0; j < tableList.size(); j++ ){
			ArrayList<ArrayList<String>> trList = tableList.get(j);
			
			// correct Table found:
			if( Pattern.matches("N o t e n s p i e g e l", trList.get(0).get(0) ) ){
				for( int i=2; i < trList.size(); i++ ){ // i=1 is only the header
					//System.out.println(i + "  " + trList.get(i));
					//if( trList.get(i).size() == 10 ){
						
						String modulText = trList.get(i).get(1);
						String semester = trList.get(i).get(2);
					
						boolean status = Pattern.matches("nicht.*", trList.get(i).get(4) )?false:true; 
						String vermerk = trList.get(i).get(7);
						String datum = trList.get(i).get(9);
						
						int modulID, versuch;
						float credits, ects, note;
						
						try{ modulID = Integer.parseInt((trList.get(i).get(0))); }
						catch ( NumberFormatException e ){ //TODO set ERROR
								modulID=0;
						}
						
						try{ 
							String tmp = trList.get(i).get(3).replace("," , ".");
							note = Float.parseFloat( tmp ); }
						catch ( NumberFormatException e ){
							//TODO set error	
							note = 0;
						}
						
						try{ ects  = Float.parseFloat(trList.get(i).get(6)); } 
							catch( NumberFormatException e ){ ects = 0; }
						try{  credits =  Float.parseFloat(trList.get(i).get(5)) ;}
							catch ( NumberFormatException e ){ credits = 0; }
						try{ versuch = Integer.parseInt(trList.get(i).get(8)); }
							catch ( NumberFormatException e){ versuch = 1; }
						
						Module m=new Module(modulID, modulText, semester, note, status, credits, ects, vermerk, versuch, datum);
						if( !modulExist(m, moduleList) ){
							moduleList.add( m );
						}// else module already exist
					//}else{
						// TODO add Error
					//	System.out.println("Error in list length");
					//}
				}
				break; 	// finished	
			}
		}
		return true;
	}
	

	//TODO Testen! // TODO wird eigentlich gar nicht gebraucht.
	/* Compares the module Arraylist with fresh Data from the QIS-Portal
	 * returns the id String if there are new marks. In this case the module Arraylist will be updated automatically.
	 * returns null if there are no new marks or if the program can't get new data from QIS.
	 * */
	public String compModulsWithFreshdata(){
		
		ArrayList <Module> module_tmp=deepcopyModule(null);
		
		if( !this.loadDataFromQIS() ){ //TODO scheint nicht zu funktionieren.
			// something was wrong, reset local data structure module
			this.moduleList=module_tmp; // i think another deep copy is not necessary
			return null;
		}
		for( Module m : this.moduleList ){
			if ( m.isNew )
				return m.id;
		}
					
		return null;
	}
	
	/* prints the new modules
	 * change the module status isNew to false if the module was new.
	 * returns true if there are new modules
	 * returns false if there are no new modules*/
	public Boolean printNewModules(ArrayList <Module> module){
		Boolean newMarks=false;
		
		for( Module m : module ){
			if( m.isNew ){
				printModul(m);
				m.isNew=false;
				newMarks=true;
			}
		}
		return newMarks;
	}
		
 	public Boolean printNewModules(){
		return printNewModules(this.moduleList);
	}
	
 	// returns null if no new modules exist, else all new modules as string
 	public String getNewModules(ArrayList <Module> module){
		String newMarks="";
		
		for( Module m : module ){
			if( m.isNew ){
				newMarks = newMarks.concat( "\n" + m.modulText + " Note: " + m.note + " Credits: " + m.credits );
				System.out.println("DEBUG: " + newMarks);
				m.isNew=false;
			}
		}
		return newMarks;
	}
 	
 	public String getNewModules(){
 		return getNewModules(this.moduleList);
 	}
	/* Only prints the current calculated statistics */
	public void printStatistics(){
		System.out.println(
				"Anzahl Credits: " + getSumCredits() +
				"\n Durchschnitt: " + getAvg() + 
				"\n Gewichteter Durchschnitt: " + getWavg() );
	}
	
	/* Calculates and Prints the statistics*/
	public void printFreshStatistics(){
		System.out.println(
				"Anzahl Credits: " + calcSumCredits() +
				"\n Durchschnitt: " + calcAverage() + 
				"\n Gewichteter Durchschnitt: " + calcWeightedAverage() );
	}
	
	/* creates a deep copy of the module arraylist or of or >original<
	 * example usage: ArrayList<Modul> copy = deepcopyModule(null);
	 */
	public ArrayList<Module> deepcopyModule(ArrayList <Module> original ){
		ArrayList <Module> copyTarget = new ArrayList<Module>();
		
		if( null == original ){
			for( Module m  : this.moduleList )
				copyTarget.add(m.deepcopyModul());
		}else{
			for( Module m : original )
				copyTarget.add(m.deepcopyModul());
		}
	
		return copyTarget;
	}
	public ArrayList<Module> deepcopyModule(){
		return deepcopyModule(null);
	}
	
	/* saveCurrentData 
	 * saves the current data to a xml file
	 * returns false if somethings was wrong 
	*/ // TODO AND NEXT bei dieser funktion stehen geblieben
	public boolean saveCurrentDataToXml(String filename){
		try {
			// 1. create the elements
			Element module = new Element("module");
			for ( Module m : this.moduleList){
				Element modul = new Element("modul");
				modul.setAttribute("id", m.id);
				
				modul.addContent(new Element("modulID").setText(String.valueOf(m.modulID)));
				modul.addContent(new Element("modulText").setText(m.modulText));
				modul.addContent(new Element("semester").setText(m.semester));
				modul.addContent(new Element("note").setText(String.valueOf(m.note)));
				modul.addContent(new Element("status").setText(String.valueOf(m.status)));
				modul.addContent(new Element("credits").setText(String.valueOf(m.credits)));
				modul.addContent(new Element("ects").setText( String.valueOf(m.ects)));
				modul.addContent(new Element("vermerk").setText(m.vermerk));
				modul.addContent(new Element("versuch").setText(String.valueOf(m.versuch)));
				modul.addContent(new Element("datum").setText(m.datum));
				modul.addContent(new Element("isNew").setText(m.isNew.toString()));
				
				module.addContent(modul);
			}
			
			Element student = new Element("student");
			student.setAttribute(new Attribute("matrikel", String.valueOf(matrikel)));
			if( null == pw) // JDOM cant save null values
				student.setAttribute(new Attribute("pw", ""));
			else
				student.setAttribute(new Attribute("pw", pw)); // TODO save encrypted
			//student.setAttribute(new Attribute("studiengang", studiengang)); // darf nicht null sein!
			//student.setAttribute(new Attribute("fachsemester", fachsemester)); // darf nicht null sein!
			student.addContent(module);
	 
			// 2. add elements to document
			Document doc = new Document(student);
			//doc.getRootElement().addContent(student);
			
			// 3. write document to file
			// new XMLOutputter().output(doc, System.out);
			XMLOutputter xmlOutput = new XMLOutputter();
	 
			// for the eyes :-)
			xmlOutput.setFormat(Format.getPrettyFormat());
			xmlOutput.output(doc, new FileWriter(filename));
			
			System.out.println("Einstellungen in " + filename + " gespeichert!");
			
		  } catch (IOException io) {
			Errors.addError("Fehler, Datei kann nicht gespeichert werden!: " + filename, 14);
			if( DEBUG )
				System.out.println(io.getMessage());
			
		  } catch ( NullPointerException np ){
			  Errors.addError("Fehler, Datei kann nicht gespeichert werden!: " + filename
					  + ".\n Stellen sie sicher das die Datenstruktur überhaupt geladen wurde."
					  , 18);
			  if( DEBUG )
				  np.printStackTrace();
		  }
		
		return true;
	}
	
	public boolean saveCurrentDataToXml(String filename, boolean saveLogin){
		if(saveLogin){
			return saveCurrentDataToXml(filename);
		}
		
		int mat_tmp = matrikel;
		String pw_tmp = pw;
		matrikel = 0;
		pw = "";
		boolean ret = saveCurrentDataToXml(filename);
		matrikel = mat_tmp;
		pw = pw_tmp;
		
		return ret;
	}
	/* loads the data from a XML file (filename) in the targetList 
	 * returns:
	 *   false if  targetList == null or if something wrong with the XML file
	 *   true if everything is OK
	 * */
	public boolean loadFromXML(String filename, ArrayList <Module> targetList){
		
		if( null == targetList ){
			return false; //TODO set Error text
		}
				
		SAXBuilder builder = new SAXBuilder();
		
		try {
			Document doc = builder.build(new File(filename));
			
			Element student = doc.getRootElement();
			Element module = student.getChild("module");
			//System.out.println("DEBUG" + moduleXML.getContentSize() + moduleXML.getContent());
			if( null == module ){
				//TODO set ERROR
				return false;
			}
			
			targetList.clear();
			
			for ( Element m : module.getChildren() ){
			
				boolean status = Pattern.matches("true", m.getChildText("status"))?true:false; 
				boolean isNew = true ;			
				int modulID, versuch;
				float credits, ects, note;
				
				try{ modulID = Integer.parseInt(m.getChildText("modulID")); }
				catch ( NumberFormatException e ){ //TODO set ERROR
						modulID=0;
				}
				try{ versuch = Integer.parseInt(m.getChildText("versuch")); }
				catch ( NumberFormatException e){ 
					versuch = 1;
					// TODO add Error
				}
				
				try{ note = Float.parseFloat( m.getChildText("note") ); }
				catch ( NumberFormatException e ){
					//TODO set error	
					note = 0;
				}
				try{ ects  = Float.parseFloat( m.getChildText("ects")); } 
					catch( NumberFormatException e ){ ects = 0; }
				try{  credits =  Float.parseFloat( m.getChildText("credits")) ;}
					catch ( NumberFormatException e ){ credits = 0; }
				
				try{ isNew = Boolean.parseBoolean(m.getChildText("isNew"));}
					catch ( Exception e){ isNew=true; }
				
				targetList.add(
					new Module(
						modulID, m.getChildText("modulText"), m.getChildText("semester"), 
						note, status, credits, ects, 
						m.getChildText("vermerk"), versuch, m.getChildText("datum"), isNew
					)
				);
			}
			
		} catch (JDOMException e) {
			Errors.addError("Fehler beim Laden der XML-Datei >" + filename + "<.", 13);
			if( DEBUG )
				e.printStackTrace();
		} catch (IOException e) {
			Errors.addError("Fehler, Datei kann nicht gefunden bzw. geöffnet werden!: " + filename, 9);
			if( DEBUG )
				e.printStackTrace();
		} 
		
		calcAll();
		return true;
	}
	
	/* overwrites the current datastructure (module) with the content from the XML file (filename)
	 * requires the method loadFromXML(String, ArrayList <Modul>) 
	*/
	public boolean loadFromXML(String filename){
		return loadFromXML(filename, this.moduleList);
	}
	/* accepts every Certificate */
	public void ignoreCertificates(){
		// Create a trust manager that does not validate certificate chains
		TrustManager[] trustAllCerts = new TrustManager[]{
			new X509TrustManager() {
				public java.security.cert.X509Certificate[] getAcceptedIssuers() {
					return null;
				}
				public void checkClientTrusted(
					java.security.cert.X509Certificate[] certs, String authType) {
				}
				public void checkServerTrusted(
					java.security.cert.X509Certificate[] certs, String authType) {
				}
			}
		};

		// Install the all-trusting trust manager
		try {
			SSLContext sc = SSLContext.getInstance("SSL");
			sc.init(null, trustAllCerts, new java.security.SecureRandom());
			HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
		} catch (Exception e) {}
	}
	
	
	/* TODO
	 *   doesn't work, very buggy.
	 * activates the Certificate check
	 */
	public void activateCertificates(){
		TrustManager[] defaultTrustManager = new TrustManager[]{
			new X509TrustManager() {
				public java.security.cert.X509Certificate[] getAcceptedIssuers() {
					return getAcceptedIssuers();
				}
				public void checkClientTrusted(
					java.security.cert.X509Certificate[] certs, String authType) {
				}
				public void checkServerTrusted(
					java.security.cert.X509Certificate[] certs, String authType) {
				}
			}
		};
		try {
			SSLContext sc = SSLContext.getInstance("SSL");
			sc.init(null, defaultTrustManager, new java.security.SecureRandom());
			HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
		}catch(Exception e) {}
	}
	
	
	/* print the source code from a website */
	public void printPageSource(String site){
		try {		
			URL url = new URL(site);
			BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
			for(String line=reader.readLine(); line != null; line=reader.readLine() ) {
				System.out.println(line);
				
			}
	        reader.close();
        
        } catch (MalformedURLException e) {
			Errors.addError("Die URL >" + site +
					"< konnte nicht aufgerufen werden.\n Eventuell haben Sie keine Internetverbindung " +
					"oder der QIS-Server ist mal wieder nicht zu erreichen.", 15);
			if( DEBUG )
				e.printStackTrace();
		} catch (IOException e) {
			Errors.addError("Unbekannter IO-Fehler, debug modus mit -d benutzen.", 16);
			if( DEBUG )
				e.printStackTrace();
		}
	}
	
	
	/* save the source code from a website in a file 
	 * 
	*/
	public void savePageSource(String site, String file){
		try {		
			URL url = new URL(site);
			BufferedWriter writer = new BufferedWriter( new FileWriter(file) );
			BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
			for(String line=reader.readLine(); line != null; line=reader.readLine() ) {
				writer.write(line);
				writer.newLine();
			}
	        reader.close();
	        writer.close();
        
        } catch (MalformedURLException e) {
        	Errors.addError("Die URL >" + site +
					"< konnte nicht aufgerufen werden.\n Eventuell haben Sie keine Internetverbindung " +
					"oder der QIS-Server ist mal wieder nicht zu erreichen.", 15);
			if( DEBUG )
				e.printStackTrace();
		} catch (IOException e) {
			Errors.addError("Fehler, Datei kann nicht gespeichert werden!: " + file, 14);
			if( DEBUG )
				e.printStackTrace();
		}
	}
	
	
	/* - checks if the input string contains a html link, with a special string like google
	 * - leave searchPattern blank to find every link
	 * - returns null if nothing can be found
	 */
	public String grepSpecialLink(String searchPattern, String input){
		Matcher matcher = Pattern.compile( "href=\".*" + searchPattern + ".*?\"" ).matcher( input );
		String link=null;
		if( true == matcher.find() ){
			link=matcher.group();
			//cut the link String
			link=link.substring( 6, link.length()-1 );
			//remove the amp; token from the string
			link = link.replace("amp;", "");
		}
		return link;
	}
	
	/* searchs in a string for a regular expression and returns the match
	 * returns null if nothing was found
	 */
	public String grepString(String searchPattern, String input){
		Matcher matcher = Pattern.compile(searchPattern).matcher(input);
		if( true == matcher.find() ){
			return matcher.group();
		}
		return null;
	}

	/* load the Studiengang and Fachsemester String from an URL*/
	public Boolean loadStudiengangAndFachsemester( URL url ){
		try {
			BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
		
			String line=reader.readLine();
			// 1 + 2 in eigenständige methode außerhalb der modulklasse! TODO
			// 1) grep Studiengang
			while( false == Pattern.matches(".*Studiengang.*?", line) ){
				line=reader.readLine();
				if( null == line ){
					Errors.addError("Studiengang konnte nicht gefunden werden!", 5 );
					return false;
				}
			}
			studiengang=grepString("<span class=\"nobr\">.*?</span>", reader.readLine());
			if( null != studiengang ){
				studiengang=studiengang.replace("&nbsp;","");
				studiengang=studiengang.substring(19,studiengang.length()-7);
			}
			
			// 2) grep Fachsemester
			while( false == Pattern.matches(".*Fachsemester.*?", line) ){
				line=reader.readLine();
				if( null == line){
					Errors.addError("Fachsemester konnte nicht gefunden werden!", 6);
					return false;
				}
			}
			fachsemester=grepString("<span class=\"nobr\">.*?</span>",reader.readLine());
			if( null != fachsemester ){
				fachsemester=fachsemester.replace("&nbsp;</span>","");
				fachsemester=fachsemester.substring(19,fachsemester.length());
				System.out.println("FACHSEMESTER: " + fachsemester);
			}
		} catch (IOException e) {
			Errors.addError("Die URL >" + url +
					"< konnte nicht aufgerufen werden.\n Eventuell haben Sie keine Internetverbindung " +
					"oder der QIS-Server ist mal wieder nicht zu erreichen.", 15);
			if( DEBUG )
				e.printStackTrace();
		}
		
		return true;
	}
	
	/* load the Studiengang and Fachsemester String from a String
	 * Note, to load from a file use: loadStudiengangAndFachsemester("file:YOURFILELOCATION");
	 * */
	public Boolean loadStudiengangAndFachsemester(String site ){
		try {
			return loadStudiengangAndFachsemester( new URL( site ) );
		} catch (MalformedURLException e) {
			Errors.addError("Die URL >" + site +
					"< konnte nicht aufgerufen werden.\n Eventuell haben Sie keine Internetverbindung " +
					"oder der QIS-Server ist mal wieder nicht zu erreichen.", 15);
			if( DEBUG )
				e.printStackTrace();
		}
		return true;
	}
	
	// test method, will be deleted later
 	public Boolean test(String site){
		String link=null;
		try {
			URL url = new URL(site);
			BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
			for(String line=reader.readLine(); line != null; line=reader.readLine() ) {
				if( null != (link=grepSpecialLink("notenspiegel", line)) )	
					break;
			}
			if (DEBUG) System.out.println("Notenspiegellink gefunden: " + link);
			if (null == link ){
				Errors.addError("Fehler, der Notenspiegellink wurde nicht gefunden. Versuchen Sie es später erneut.", 3);
			}
		} catch (MalformedURLException e) {
			Errors.addError("Die URL >" + site +
					"< konnte nicht aufgerufen werden.\n Eventuell haben Sie keine Internetverbindung " +
					"oder der QIS-Server ist mal wieder nicht zu erreichen.", 15);
			if( DEBUG )
				e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		if ( null == link ){
			System.out.println("Fehler, nix gefunden");
			return false;
		}
		return true;
	}
	
 	/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ calculate and output methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
	//TODO printModul als Tabelle und als Kurzform, printPassed, printNotPassed
	//TODO farbänderung bestanden !bestanden
	public void printModuls(){
		for( Module m : moduleList ){
			System.out.println("--------------------" + m.modulID + "--------------------");
			System.out.println("Prüfungsnummer: " + m.modulID); 
			System.out.println("Prüfungstext: " + m.modulText);
			System.out.println("Semester: " + m.semester);
			System.out.println("Note: " + m.note);
			System.out.println("Status: " + ((m.status)?"bestanden :D":"failed"));
			System.out.println("Credits: " + m.credits);
			System.out.println("ECTS: " + m.ects);
			System.out.println("Vermerk: " + m.vermerk);
			System.out.println("Versuch: " + m.versuch);
			System.out.println("Prüfungsdatum: " + m.datum);
			System.out.println("--------------------------------------------\n");
			m.isNew=false;
		}
	}
	/*prints the modul m*/
	public void printModul(Module m){
		System.out.println("--------------------" + m.modulID + "--------------------");
		System.out.println("Prüfungsnummer: " + m.modulID); 
		System.out.println("Prüfungstext: " + m.modulText);
		System.out.println("Semester: " + m.semester);
		System.out.println("Note: " + m.note);
		System.out.println("Status: " + ((m.status)?"bestanden :D":"failed"));
		System.out.println("Credits: " + m.credits);
		System.out.println("ECTS: " + m.ects);
		System.out.println("Vermerk: " + m.vermerk);
		System.out.println("Versuch: " + m.versuch);
		System.out.println("Prüfungsdatum: " + m.datum);
		System.out.println("--------------------------------------------\n");
		m.isNew=false;
	}
	
	/* calls all calcRoutines in a thread. 
	 * Attention! Doesn't wait other Components!
	 */
	public void calcAll(){
		new Thread(){
			public void run(){
				calcAverage();
				calcStatisticsBySemester();
				calcSumCredits();
				calcWeightedAverage();
			}
		}.start();
	}
	/* calculates the normal average
	 * will return 0 if something is wrong
	*/
	public double calcAverage(){
		double sum=0;
		int i=0;
		for( Module m : moduleList ){
			if( m.credits != 0 && m.note >= 1.0 && m.note < 5.0 ){
				sum+=m.note;
				i++;
			}
		}
		this.avg=sum/i;
		return this.avg;
	}
	
	/* calculates the average weighted by credits
	 * will return 0 if something is wrong
	*/
	public double calcWeightedAverage(){
		double divisor=0;
		int sumCredits=0;
		for( Module m : moduleList ){
			if( m.credits != 0 && m.note >= 1.0 && m.note < 5.0 ){
				divisor+=(m.note*m.credits);
				sumCredits+=m.credits;
			}
		}
		this.wavg=divisor/sumCredits;
		return this.wavg;
	}
	
	/* fills the ArrayList semNoten and returns it */
	public ArrayList<SemNote> calcStatisticsBySemester(){
		boolean exists=false;
		semNoten = new ArrayList<SemNote>(); // clear the old
		for( Module m : moduleList ){
			// module contains a valid mark?
			if( m.credits != 0 && m.note >= 1.0 && m.note <= 5.0 ){
				exists=false;
				// check if semester was already found
				for( SemNote s : semNoten ){
					if(s.semester.equals(m.semester)){
						s.add(m.note, m.credits);
						exists=true;
						break;
					}
				}
				// semester not found, add new
				if(!exists){
					//System.out.println(m.semester + " " + m.note + " " + m.credits);
					semNoten.add(new SemNote(m.semester, m.note, m.credits));
				}
			}	
		}
		return semNoten;
	}

	/* returns the summation of credits
	 * will return 0 if something is wrong
	*/
 	public int calcSumCredits(){
		this.sumCredits=0;
		for( Module m : moduleList )
			sumCredits+=m.credits;
		return sumCredits;
	}
	
	/* returns true if a module with status isNew exist.
	 * returns false if no new module exist in module list
	 */
	public boolean checkListForNewModules(ArrayList<Module> modules ) {
		for( Module m : modules ){
			if( m.isNew )
				return true;
		}
		return false;
	}
	public boolean checkListForNewModules(){
		return checkListForNewModules(this.moduleList);
	}
	
	/* returns null on failure
	 * returns a module as Object if everything is fine
	 */
	protected Object[] getModuleRaw(int i){
		Object[] modulesTable = {
				0000, "Modultext", "Semester", 0.00, "Status", 0000, "Vermerk", 0, "Datum", "ECTS"
		};
		
		if( i >= moduleList.size() )
			return null;
		
		Module m = moduleList.get(i);
		modulesTable[0] = m.modulID;
		modulesTable[1] = m.modulText;
		modulesTable[2] = m.semester;
		modulesTable[3] = m.note;
		modulesTable[4] = m.status;
		modulesTable[5] = m.credits;
		modulesTable[6] = m.vermerk;
		modulesTable[7] = m.versuch;
		modulesTable[8] = m.datum;
		modulesTable[9] = m.ects;
		
		return modulesTable;
	}
	
	public int getModuleListSize(){
		if( null != moduleList )
			return moduleList.size();
		return 0;
	}

}

